"use strict";
/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseMode = void 0;
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const child_process = __importStar(require("child_process"));
const init_koala_modules_1 = require("../init/init_koala_modules");
const pre_define_1 = require("../pre_define");
const utils_1 = require("../util/utils");
const plugins_driver_1 = require("../plugins/plugins_driver");
const logger_1 = require("../logger");
const dependency_analyzer_1 = require("../dependency_analyzer");
const error_1 = require("../util/error");
const types_1 = require("../types");
const generate_arktsconfig_1 = require("./generate_arktsconfig");
const KitImportTransformer_1 = require("../plugins/KitImportTransformer");
const record_time_mem_1 = require("../util/record_time_mem");
const dotGraphDump_1 = require("../util/dotGraphDump");
class BaseMode {
    constructor(buildConfig) {
        this.declFileMap = new Map();
        this.buildConfig = buildConfig;
        this.entryFiles = new Set(buildConfig.compileFiles);
        this.fileToModule = new Map();
        this.moduleInfos = new Map();
        this.mergedAbcFile = path.resolve(this.outputDir, pre_define_1.MERGED_ABC_FILE);
        this.logger = logger_1.Logger.getInstance();
        this.depAnalyzer = new dependency_analyzer_1.DependencyAnalyzer(this.buildConfig);
        this.abcFiles = new Set();
        this.jobs = {};
        this.jobQueue = [];
        this.completedJobQueue = [];
        this.koalaModule = (0, init_koala_modules_1.initKoalaModules)(buildConfig);
        this.processBuildConfig();
        this.backwardCompatibilityWorkaroundStub();
        this.loadDeclFileMap();
    }
    loadDeclFileMap() {
        const declMapFile = path.join(this.cacheDir, pre_define_1.DECL_FILE_MAP_NAME);
        if (!fs.existsSync(declMapFile)) {
            return;
        }
        try {
            const content = fs.readFileSync(declMapFile, 'utf-8');
            const data = JSON.parse(content);
            for (const [key, value] of Object.entries(data)) {
                this.declFileMap.set(key, value);
            }
        }
        catch (error) {
            this.logger.printInfo('Failed to load decl file map, will create new one.');
        }
    }
    saveDeclFileMap() {
        const declMapFile = path.join(this.cacheDir, pre_define_1.DECL_FILE_MAP_NAME);
        const data = {};
        this.declFileMap.forEach((value, key) => {
            data[key] = value;
        });
        (0, utils_1.ensurePathExists)(declMapFile);
        fs.writeFileSync(declMapFile, JSON.stringify(data, null, 2));
    }
    updateDeclFileMap(fileInfo) {
        const { declEtsOutputPath, glueCodeOutputPath } = this.getOutputFilePaths(fileInfo);
        try {
            const declStat = fs.statSync(declEtsOutputPath);
            const glueCodeStat = fs.statSync(glueCodeOutputPath);
            this.declFileMap.set(fileInfo.inputFilePath, {
                delFilePath: declEtsOutputPath,
                declLastModified: declStat.mtimeMs,
                glueCodeFilePath: glueCodeOutputPath,
                glueCodeLastModified: glueCodeStat.mtimeMs,
                sourceFilePath: fileInfo.inputFilePath
            });
            this.logger.printInfo(`Updated decl file map for ${fileInfo.inputFilePath} with timestamp: ${declStat.mtimeMs}`);
        }
        catch (error) {
            this.logger.printInfo(`Failed to update decl file map for ${fileInfo.inputFilePath}`);
        }
    }
    getOutputFilePaths(fileInfo) {
        const moduleInfo = this.moduleInfos.get(fileInfo.inputFilePath);
        const filePathFromModuleRoot = path.relative(moduleInfo.moduleRootPath, fileInfo.inputFilePath);
        const declEtsOutputPath = (0, utils_1.changeDeclgenFileExtension)(path.join(moduleInfo.declgenV1OutPath, moduleInfo.packageName, filePathFromModuleRoot), pre_define_1.DECL_ETS_SUFFIX);
        const glueCodeOutputPath = (0, utils_1.changeDeclgenFileExtension)(path.join(moduleInfo.declgenBridgeCodePath, moduleInfo.packageName, filePathFromModuleRoot), pre_define_1.TS_SUFFIX);
        (0, utils_1.ensurePathExists)(declEtsOutputPath);
        (0, utils_1.ensurePathExists)(glueCodeOutputPath);
        return { declEtsOutputPath, glueCodeOutputPath };
    }
    needsBackup(fileInfo) {
        const { declEtsOutputPath, glueCodeOutputPath } = this.getOutputFilePaths(fileInfo);
        let needsDeclBackup = false;
        let needsGlueCodeBackup = false;
        try {
            const declInfo = this.declFileMap.get(fileInfo.inputFilePath);
            if (declInfo && fs.existsSync(declEtsOutputPath)) {
                const declModified = fs.statSync(declEtsOutputPath).mtimeMs;
                if (declModified !== declInfo.declLastModified) {
                    needsDeclBackup = true;
                }
            }
            if (declInfo && fs.existsSync(glueCodeOutputPath)) {
                const glueCodeModified = fs.statSync(glueCodeOutputPath).mtimeMs;
                if (declInfo.glueCodeLastModified && glueCodeModified !== declInfo.glueCodeLastModified) {
                    needsGlueCodeBackup = true;
                }
            }
        }
        catch (error) {
            needsDeclBackup = true;
            needsGlueCodeBackup = true;
        }
        return { needsDeclBackup, needsGlueCodeBackup };
    }
    backupFiles(fileInfo, backupDecl = true, backupGlueCode = true) {
        const { declEtsOutputPath, glueCodeOutputPath } = this.getOutputFilePaths(fileInfo);
        if (backupDecl && fs.existsSync(declEtsOutputPath)) {
            const delEtsbackupPath = `${declEtsOutputPath}.backup`;
            fs.copyFileSync(declEtsOutputPath, delEtsbackupPath);
        }
        if (backupGlueCode && fs.existsSync(glueCodeOutputPath)) {
            const glueCodeBackupPath = `${glueCodeOutputPath}.backup`;
            fs.copyFileSync(glueCodeOutputPath, glueCodeBackupPath);
        }
    }
    preprocessBackupForParallel() {
        this.logger.printInfo('Starting backup preprocessing for parallel generation');
        this.completedJobQueue.forEach((jobInfo) => {
            try {
                const backupNeeds = this.needsBackup(jobInfo.compileFileInfo);
                if (backupNeeds.needsDeclBackup || backupNeeds.needsGlueCodeBackup) {
                    // 使用统一的备份方法
                    this.backupFiles(jobInfo.compileFileInfo);
                }
            }
            catch (error) {
                this.logger.printInfo(`Backup preprocessing failed for ${jobInfo.compileFileInfo.inputFilePath}: ${error}`);
            }
        });
    }
    get abcLinkerPath() {
        return this.buildConfig.abcLinkerPath;
    }
    get hasMainModule() {
        return this.buildConfig.hasMainModule;
    }
    get useEmptyPackage() {
        return this.buildConfig.useEmptyPackage ?? false;
    }
    get frameworkMode() {
        return this.buildConfig.frameworkMode ?? false;
    }
    get genDeclAnnotations() {
        return this.buildConfig.genDeclAnnotations ?? true;
    }
    get skipDeclCheck() {
        return this.buildConfig.skipDeclCheck ?? true;
    }
    get es2pandaMode() {
        return this.buildConfig.es2pandaMode;
    }
    get es2pandaDepGraphDotDump() {
        return this.buildConfig.es2pandaDepGraphDotDump;
    }
    get entryFile() {
        return this.buildConfig.entryFile;
    }
    get mainPackageName() {
        return this.buildConfig.packageName;
    }
    get mainModuleRootPath() {
        return this.buildConfig.moduleRootPath;
    }
    get mainModuleType() {
        return this.buildConfig.moduleType;
    }
    get outputDir() {
        return this.buildConfig.loaderOutPath;
    }
    get cacheDir() {
        return this.buildConfig.cachePath;
    }
    get dependencyModuleList() {
        return this.buildConfig.dependencyModuleList;
    }
    get enableDeclgenEts2Ts() {
        return this.buildConfig.enableDeclgenEts2Ts;
    }
    get isBuildConfigModified() {
        return this.buildConfig.isBuildConfigModified;
    }
    set isBuildConfigModified(modified) {
        this.buildConfig.isBuildConfigModified = modified;
    }
    get byteCodeHar() {
        return this.buildConfig.byteCodeHar;
    }
    get mainSourceRoots() {
        return this.buildConfig.sourceRoots;
    }
    get declgenV1OutPath() {
        return this.buildConfig.declgenV1OutPath;
    }
    get declgenV2OutPath() {
        return this.buildConfig.declgenV2OutPath;
    }
    get declgenBridgeCodePath() {
        return this.buildConfig.declgenBridgeCodePath;
    }
    get isDebug() {
        return this.buildConfig.buildMode === types_1.BUILD_MODE.DEBUG;
    }
    compile(job) {
        this.logger.printDebug("compile START");
        this.logger.printDebug(`job ${JSON.stringify(job, null, 1)}`);
        const { inputFilePath, outputFilePath } = job.compileFileInfo;
        const outputDeclFilePath = (0, utils_1.changeFileExtension)(outputFilePath, pre_define_1.DECL_ETS_SUFFIX);
        (0, utils_1.ensurePathExists)(outputDeclFilePath);
        const source = fs.readFileSync(inputFilePath, 'utf-8');
        const ets2pandaCmd = (0, utils_1.formEts2pandaCmd)(job, this.isDebug);
        this.logger.printDebug('ets2pandaCmd: ' + ets2pandaCmd.join(' '));
        const { arkts, arktsGlobal } = this.koalaModule;
        try {
            arktsGlobal.filePath = inputFilePath;
            arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer;
            arktsGlobal.compilerContext = arkts.Context.createFromString(source);
            plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program);
            arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer);
            this.logger.printDebug('es2panda proceedToState parsed');
            let ast = arkts.EtsScript.fromContext();
            if (this.buildConfig.aliasConfig && Object.keys(this.buildConfig.aliasConfig).length > 0) {
                // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin
                this.logger.printDebug('Transforming import statements with alias config');
                let transformAst = new KitImportTransformer_1.KitImportTransformer(arkts, arktsGlobal.compilerContext.program, this.buildConfig.buildSdkPath, this.buildConfig.aliasConfig).transform(ast);
                plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst);
            }
            else {
                plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
            }
            plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.PARSED);
            this.logger.printInfo('plugin parsed finished');
            arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer);
            this.logger.printInfo('es2panda proceedToState checked');
            if (job.type & types_1.CompileJobType.DECL) {
                // Generate 1.2 declaration files(a temporary solution while binary import not pushed)
                arkts.generateStaticDeclarationsFromContext(outputDeclFilePath);
                this.logger.printDebug("compile FINISH [DECL]");
            }
            if (job.type & types_1.CompileJobType.ABC) {
                ast = arkts.EtsScript.fromContext();
                plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
                plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.CHECKED);
                this.logger.printInfo('plugin checked finished');
                arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer);
                this.logger.printInfo('es2panda bin generated');
                this.logger.printDebug("compile FINISH [ABC]");
            }
        }
        catch (error) {
            if (error instanceof Error) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, 'Compile abc files failed.', error.message, inputFilePath));
            }
        }
        finally {
            plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.CLEAN);
            arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer);
            arkts.destroyConfig(arktsGlobal.config);
        }
    }
    async run() {
        this.jobs = this.depAnalyzer.collectJobs(this.entryFiles, this.fileToModule, this.moduleInfos);
        if (this.es2pandaDepGraphDotDump) {
            fs.writeFileSync(path.resolve(this.cacheDir, 'graph.dot'), (0, dotGraphDump_1.dotGraphDump)(this.jobs), 'utf-8');
        }
        this.initCompileQueues();
        while (this.haveQueuedJobs()) {
            let job = this.consumeJob();
            if (job.fileList.length > 1) {
                // Compile cycle simultaneous
                this.logger.printDebug("Compiling cycle....");
                this.logger.printDebug(`file list: \n${job.fileList.join('\n')}`);
                this.compileSimultaneous(job);
            }
            else {
                this.compile(job);
            }
            this.dispatchNextJob(job);
        }
        if (this.completedJobQueue.length > 0) {
            this.mergeAbcFiles();
        }
    }
    compileSimultaneous(job) {
        let compileSingleData = new record_time_mem_1.CompileSingleData(path.join(path.resolve(), record_time_mem_1.BS_PERF_FILE_NAME));
        compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.PROCEED_PARSE);
        this.logger.printDebug(`job ${JSON.stringify(job, null, 1)}`);
        const ets2pandaCmd = (0, utils_1.formEts2pandaCmd)(job, this.isDebug, true);
        this.logger.printDebug('ets2pandaCmd: ' + ets2pandaCmd.join(' '));
        let errorFlag = false;
        let { arkts, arktsGlobal } = this.koalaModule;
        try {
            arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer;
            arktsGlobal.compilerContext = arkts.Context.createContextGenerateAbcForExternalSourceFiles(job.fileList);
            plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program);
            arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer);
            this.logger.printInfo('es2panda proceedToState parsed');
            compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.PLUGIN_PARSE, record_time_mem_1.RECORDE_COMPILE_NODE.PROCEED_PARSE);
            let ast = arkts.EtsScript.fromContext();
            if (this.buildConfig.aliasConfig && Object.keys(this.buildConfig.aliasConfig).length > 0) {
                // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin
                this.logger.printInfo('Transforming import statements with alias config');
                let transformAst = new KitImportTransformer_1.KitImportTransformer(arkts, arktsGlobal.compilerContext.program, this.buildConfig.buildSdkPath, this.buildConfig.aliasConfig).transform(ast);
                plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst);
            }
            else {
                plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
            }
            plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
            plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.PARSED);
            this.logger.printInfo('plugin parsed finished');
            compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.PROCEED_CHECK, record_time_mem_1.RECORDE_COMPILE_NODE.PLUGIN_PARSE);
            arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer);
            this.logger.printInfo('es2panda proceedToState checked');
            compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.PLUGIN_CHECK, record_time_mem_1.RECORDE_COMPILE_NODE.PROCEED_CHECK);
            // NOTE: workaround to build arkoala arkui
            // NOTE: to be refactored
            if (job.type & types_1.CompileJobType.DECL) {
                for (const file of job.fileList) {
                    const module = this.fileToModule.get(file);
                    const declEtsOutputPath = (0, utils_1.changeFileExtension)(path.resolve(this.cacheDir, module.packageName, path.relative(module.moduleRootPath, file)), pre_define_1.DECL_ETS_SUFFIX);
                    (0, utils_1.ensurePathExists)(declEtsOutputPath);
                    arkts.generateStaticDeclarationsFromContext(declEtsOutputPath);
                }
            }
            if (job.type & types_1.CompileJobType.ABC) {
                ast = arkts.EtsScript.fromContext();
                plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
                plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.CHECKED);
                this.logger.printInfo('plugin checked finished');
                compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.BIN_GENERATE, record_time_mem_1.RECORDE_COMPILE_NODE.PLUGIN_CHECK);
                arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer);
                this.logger.printInfo('es2panda bin generated');
                compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.CFG_DESTROY, record_time_mem_1.RECORDE_COMPILE_NODE.BIN_GENERATE);
            }
        }
        catch (error) {
            errorFlag = true;
            if (error instanceof Error) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, 'Compile abc files failed.', error.message, job.compileFileInfo.inputFilePath));
            }
        }
        finally {
            if (!errorFlag) {
                arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer);
            }
            plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.CLEAN);
            arkts.destroyConfig(arktsGlobal.config);
            compileSingleData.record(record_time_mem_1.RECORDE_COMPILE_NODE.END, record_time_mem_1.RECORDE_COMPILE_NODE.CFG_DESTROY);
            compileSingleData.writeSumSingle(path.resolve());
        }
    }
    mergeAbcFiles() {
        let linkerInputFile = path.join(this.cacheDir, pre_define_1.LINKER_INPUT_FILE);
        let linkerInputContent = '';
        this.completedJobQueue.forEach((job) => {
            if (job.compileFileInfo.outputFilePath.endsWith(pre_define_1.ABC_SUFFIX)) {
                linkerInputContent += job.compileFileInfo.outputFilePath + os.EOL;
            }
        });
        fs.writeFileSync(linkerInputFile, linkerInputContent);
        let abcLinkerCmd = ['"' + this.abcLinkerPath + '"'];
        abcLinkerCmd.push('--strip-unused');
        abcLinkerCmd.push('--output');
        abcLinkerCmd.push('"' + this.mergedAbcFile + '"');
        abcLinkerCmd.push('--');
        abcLinkerCmd.push('@' + '"' + linkerInputFile + '"');
        let abcLinkerCmdStr = abcLinkerCmd.join(' ');
        if ((0, utils_1.isMac)()) {
            const loadLibrary = 'DYLD_LIBRARY_PATH=' + '"' + process.env.DYLD_LIBRARY_PATH + '"';
            abcLinkerCmdStr = loadLibrary + ' ' + abcLinkerCmdStr;
        }
        this.logger.printDebug(abcLinkerCmdStr);
        (0, utils_1.ensurePathExists)(this.mergedAbcFile);
        try {
            child_process.execSync(abcLinkerCmdStr).toString();
        }
        catch (error) {
            if (error instanceof Error) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_LINK_ABC_FAIL, 'Link abc files failed.', error.message));
            }
        }
    }
    getDependencyModules(moduleInfo) {
        const dynamicDependencyModules = new Map();
        const staticDependencyModules = new Map();
        // NOTE: workaround
        // NOTE: to be refactored
        this.processDependencyModule(moduleInfo.packageName, moduleInfo, dynamicDependencyModules, staticDependencyModules);
        if (moduleInfo.dependencies) {
            moduleInfo.dependencies.forEach((packageName) => {
                let dependency = this.moduleInfos.get(packageName);
                if (!dependency) {
                    throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_FOUND, `Module ${packageName} is not found in dependencyModuleList`));
                }
                this.processDependencyModule(packageName, dependency, dynamicDependencyModules, staticDependencyModules);
            });
        }
        return [dynamicDependencyModules, staticDependencyModules];
    }
    processDependencyModule(packageName, module, dynamicDependencyModules, staticDependencyModules) {
        if (module.language === pre_define_1.LANGUAGE_VERSION.ARKTS_1_2) {
            staticDependencyModules.set(packageName, module);
        }
        else if (module.language === pre_define_1.LANGUAGE_VERSION.ARKTS_1_1) {
            dynamicDependencyModules.set(packageName, module);
        }
        else if (module.language === pre_define_1.LANGUAGE_VERSION.ARKTS_HYBRID) {
            staticDependencyModules.set(packageName, module);
            dynamicDependencyModules.set(packageName, module);
        }
    }
    generateArkTSConfigForModules() {
        // Just to init the generator
        generate_arktsconfig_1.ArkTSConfigGenerator.getInstance(this.buildConfig);
        const dependenciesSets = new Map;
        // Fill dependenciesSets and generate ArktsConfigs
        this.moduleInfos.forEach((moduleInfo) => {
            dependenciesSets.set(moduleInfo.packageName, new Set());
            moduleInfo.dependencies?.forEach((dependency) => {
                dependenciesSets.get(moduleInfo.packageName).add(this.moduleInfos.get(dependency));
            });
            generate_arktsconfig_1.ArkTSConfigGenerator.getInstance().generateArkTSConfigFile(moduleInfo, this.enableDeclgenEts2Ts);
        });
        // Merge ArktsConfigs
        dependenciesSets.forEach((dependencies, module) => {
            let moduleInfo = this.moduleInfos.get(module);
            let arktsConfig = generate_arktsconfig_1.ArkTSConfigGenerator.getInstance().getArktsConfigByPackageName(module);
            dependencies.forEach((dependency) => {
                arktsConfig.mergeArktsConfig(generate_arktsconfig_1.ArkTSConfigGenerator.getInstance().getArktsConfigByPackageName(dependency.packageName));
            });
            fs.writeFileSync(moduleInfo.arktsConfigFile, JSON.stringify(arktsConfig.object, null, 2));
        });
    }
    collectModuleDependencies() {
        this.moduleInfos.forEach((moduleInfo) => {
            let [dynamicDepModules, staticDepModules] = this.getDependencyModules(moduleInfo);
            moduleInfo.dynamicDependencyModules = dynamicDepModules;
            moduleInfo.staticDependencyModules = staticDepModules;
        });
    }
    collectModuleInfos() {
        // NOTE: workaround for frameworkMode
        if (this.hasMainModule && (!this.mainPackageName || !this.mainModuleRootPath || !this.mainSourceRoots)) {
            throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_MODULE_INFO_NOT_CORRECT_FAIL, 'Main module info is not correct.'));
        }
        const mainModuleInfo = this.getMainModuleInfo();
        this.moduleInfos.set(this.mainPackageName, mainModuleInfo);
        this.dependencyModuleList.forEach((dependency) => {
            if (!(0, utils_1.checkDependencyModuleInfoCorrectness)(dependency)) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_DEPENDENT_MODULE_INFO_NOT_CORRECT_FAIL, 'Dependent module info is not correct.'));
            }
            if (this.moduleInfos.has(dependency.packageName)) {
                return;
            }
            // NOTE: workaround
            // NOTE: to be refactored
            const getNormalizedEntryFile = (dependency) => {
                if (path.isAbsolute(dependency.entryFile)) {
                    return path.relative(dependency.modulePath, dependency.entryFile);
                }
                return dependency.entryFile;
            };
            let moduleInfo = {
                isMainModule: false,
                packageName: dependency.packageName,
                moduleRootPath: dependency.modulePath,
                moduleType: dependency.moduleType,
                sourceRoots: dependency.sourceRoots,
                entryFile: getNormalizedEntryFile(dependency),
                arktsConfigFile: path.resolve(this.cacheDir, dependency.packageName, pre_define_1.ARKTSCONFIG_JSON_FILE),
                dynamicDependencyModules: new Map(),
                staticDependencyModules: new Map(),
                declgenV1OutPath: dependency.declgenV1OutPath,
                declgenV2OutPath: dependency.declgenV2OutPath,
                declgenBridgeCodePath: dependency.declgenBridgeCodePath,
                language: dependency.language,
                declFilesPath: dependency.declFilesPath,
                dependencies: dependency.dependencies ?? [],
                byteCodeHar: dependency.byteCodeHar,
                abcPath: dependency.abcPath,
            };
            this.moduleInfos.set(dependency.packageName, moduleInfo);
            this.moduleInfos.get(this.mainPackageName).dependencies.push(dependency.packageName);
        });
        this.collectModuleDependencies();
    }
    getMainModuleInfo() {
        return {
            isMainModule: true,
            packageName: this.mainPackageName,
            moduleRootPath: this.mainModuleRootPath,
            moduleType: this.mainModuleType,
            sourceRoots: this.mainSourceRoots,
            // NOTE: workaround. (entryFile is almost always undefined)
            // NOTE: to be refactored
            entryFile: this.entryFile ?? '',
            arktsConfigFile: path.resolve(this.cacheDir, this.mainPackageName, pre_define_1.ARKTSCONFIG_JSON_FILE),
            dynamicDependencyModules: new Map(),
            staticDependencyModules: new Map(),
            declgenV1OutPath: this.declgenV1OutPath,
            declgenV2OutPath: this.declgenV2OutPath,
            declgenBridgeCodePath: this.declgenBridgeCodePath,
            byteCodeHar: this.byteCodeHar,
            language: pre_define_1.LANGUAGE_VERSION.ARKTS_1_2,
            dependencies: []
        };
    }
    processEntryFiles() {
        this.entryFiles.forEach((file) => {
            for (const [_, moduleInfo] of this.moduleInfos) {
                const relativePath = path.relative(moduleInfo.moduleRootPath, file);
                if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
                    continue;
                }
                this.fileToModule.set(path.resolve(file), moduleInfo);
                return;
            }
            throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_FILE_NOT_BELONG_TO_ANY_MODULE_FAIL, 'File does not belong to any module in moduleInfos.', '', file));
        });
        this.logger.printDebug(`collected fileToModule ${JSON.stringify([...this.fileToModule.entries()], null, 1)}`);
    }
    processBuildConfig() {
        let compileSingleData = new record_time_mem_1.CompileSingleData(path.join(path.resolve(), record_time_mem_1.BS_PERF_FILE_NAME));
        compileSingleData.record(record_time_mem_1.RECORDE_MODULE_NODE.COLLECT_INFO);
        this.collectModuleInfos();
        this.logger.printDebug(`ModuleInfos: ${JSON.stringify([...this.moduleInfos], null, 1)}`);
        compileSingleData.record(record_time_mem_1.RECORDE_MODULE_NODE.GEN_CONFIG, record_time_mem_1.RECORDE_MODULE_NODE.COLLECT_INFO);
        this.generateArkTSConfigForModules();
        compileSingleData.record(record_time_mem_1.RECORDE_MODULE_NODE.CLT_FILES, record_time_mem_1.RECORDE_MODULE_NODE.GEN_CONFIG);
        this.processEntryFiles();
        compileSingleData.record(record_time_mem_1.RECORDE_MODULE_NODE.END, record_time_mem_1.RECORDE_MODULE_NODE.CLT_FILES);
        compileSingleData.writeSumSingle(path.resolve());
    }
    backwardCompatibilityWorkaroundStub() {
        const mainModule = this.moduleInfos.get(this.mainPackageName);
        // NOTE: workaround (just to add entryFile to mainModule)
        // NOTE: to be refactored
        if (Object.keys(this.jobs).length == 0) {
            const mainModuleFileList = [...this.fileToModule.entries()].filter(([_, module]) => {
                return module.isMainModule;
            }).map(([file, _]) => { return file; });
            mainModule.entryFile = mainModuleFileList[0];
        }
        else {
            mainModule.entryFile = Object.entries(this.jobs).filter(([_, job]) => {
                return job.jobDependants.length == 0;
            })[0][1].fileList[0];
        }
        this.logger.printDebug(`mainModule entryFile: ${mainModule.entryFile}`);
    }
    async runSimultaneous() {
        let compileSingleData = new record_time_mem_1.CompileSingleData(path.join(path.resolve(), record_time_mem_1.BS_PERF_FILE_NAME));
        compileSingleData.record(record_time_mem_1.RECORDE_RUN_NODE.GEN_MODULE);
        const mainModule = this.moduleInfos.get(this.mainPackageName);
        // NOTE: workaround (main module entry file problem)
        // NOTE: to be refactored
        let entryFile;
        let module;
        if (!mainModule || !mainModule.entryFile) {
            entryFile = [...this.entryFiles][0];
            module = this.fileToModule.get(entryFile);
        }
        else {
            entryFile = mainModule.entryFile;
            module = mainModule;
        }
        let outputFile = this.mergedAbcFile;
        (0, utils_1.ensurePathExists)(outputFile);
        let arktsConfigFile = module.arktsConfigFile;
        this.logger.printDebug(`entryFile: ${entryFile}`);
        this.logger.printDebug(`module: ${JSON.stringify(module, null, 1)}`);
        this.logger.printDebug(`arktsConfigFile: ${arktsConfigFile}`);
        // We do not need any queues just compile a bunch of files
        // Ets2panda will build it simultaneous
        this.compileSimultaneous({
            id: outputFile,
            fileList: [...this.entryFiles],
            jobDependencies: [],
            jobDependants: [],
            compileFileInfo: {
                inputFilePath: entryFile,
                outputFilePath: outputFile,
                arktsConfigFile: arktsConfigFile
            },
            type: types_1.CompileJobType.ABC
        });
        compileSingleData.record(record_time_mem_1.RECORDE_RUN_NODE.END, record_time_mem_1.RECORDE_RUN_NODE.COMPILE_FILES);
        compileSingleData.writeSumSingle(path.resolve());
    }
    async runParallel() {
        // NOTE: TBD
        throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, 'Parallel mode is currently unavailable.'));
    }
    addJobToQueue(job) {
        if (this.jobQueue.some((queuedJob) => queuedJob.id === job.id)) {
            this.logger.printWarn(`Detected job duplication: job.id == ${job.id}`);
            return;
        }
        this.logger.printDebug(`Added Job ${JSON.stringify(job, null, 1)} to the queue`);
        this.jobQueue.push(job);
    }
    initCompileQueues() {
        Object.values(this.jobs).forEach((job) => {
            if (job.jobDependencies.length === 0) {
                this.addJobToQueue(job);
            }
        });
    }
    dispatchNextJob(completedJob) {
        let completedJobId = completedJob.id;
        this.logger.printDebug(`Removed Job ${completedJobId} from the queue`);
        completedJob.jobDependants.forEach((dependantJobId) => {
            const depJob = this.jobs[dependantJobId];
            const depIndex = depJob.jobDependencies.indexOf(completedJobId);
            if (depIndex !== -1) {
                depJob.jobDependencies.splice(depIndex, 1);
                if (depJob.jobDependencies.length === 0) {
                    this.addJobToQueue(depJob);
                }
                else {
                    this.logger.printDebug(`Job ${depJob.id} still have dependencies ${JSON.stringify(depJob.jobDependencies, null, 1)}`);
                }
            }
        });
        this.completedJobQueue.push(completedJob);
    }
    haveQueuedJobs() {
        return (this.jobQueue.length > 0);
    }
    consumeJob() {
        if (this.jobQueue.length == 0) {
            this.logger.printDebug("Job queue is empty!");
            return null;
        }
        return this.jobQueue.shift();
    }
    async runConcurrent() {
        // NOTE: TBD
        throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_COMPILE_ABC_FAIL, 'Concurrent mode is currently unavailable.'));
    }
    async generateDeclaration() {
        this.loadDeclFileMap();
        this.preprocessBackupForParallel();
        this.jobs = this.depAnalyzer.collectJobs(this.entryFiles, this.fileToModule, this.moduleInfos);
        this.initCompileQueues();
        while (this.haveQueuedJobs()) {
            let job = this.consumeJob();
            this.declgen(job);
            this.dispatchNextJob(job);
        }
        this.completedJobQueue.forEach((jobInfo) => {
            this.updateDeclFileMap(jobInfo.compileFileInfo);
        });
        this.saveDeclFileMap();
    }
    declgen(jobInfo) {
        const inputFilePath = jobInfo.compileFileInfo.inputFilePath;
        const source = fs.readFileSync(inputFilePath, 'utf8');
        const moduleInfo = this.fileToModule.get(inputFilePath);
        const filePathFromModuleRoot = path.relative(moduleInfo.moduleRootPath, inputFilePath);
        const declEtsOutputPath = (0, utils_1.changeDeclgenFileExtension)(path.join(moduleInfo.declgenV1OutPath, moduleInfo.packageName, filePathFromModuleRoot), pre_define_1.DECL_ETS_SUFFIX);
        const etsOutputPath = (0, utils_1.changeDeclgenFileExtension)(path.join(moduleInfo.declgenBridgeCodePath, moduleInfo.packageName, filePathFromModuleRoot), pre_define_1.TS_SUFFIX);
        (0, utils_1.ensurePathExists)(declEtsOutputPath);
        (0, utils_1.ensurePathExists)(etsOutputPath);
        let { arkts, arktsGlobal } = this.koalaModule;
        try {
            const staticRecordPath = path.join(moduleInfo.declgenV1OutPath, pre_define_1.STATIC_RECORD_FILE);
            const declEtsOutputDir = path.dirname(declEtsOutputPath);
            const staticRecordRelativePath = (0, utils_1.changeFileExtension)(path.relative(declEtsOutputDir, staticRecordPath).replace(/\\/g, '\/'), "", pre_define_1.DECL_TS_SUFFIX);
            (0, utils_1.createFileIfNotExists)(staticRecordPath, pre_define_1.STATIC_RECORD_FILE_CONTENT);
            let ets2pandaCmd = (0, utils_1.formEts2pandaCmd)(jobInfo);
            this.logger.printDebug(`ets2panda cmd: ${ets2pandaCmd.join(' ')}`);
            arktsGlobal.filePath = inputFilePath;
            arktsGlobal.config = arkts.Config.create(ets2pandaCmd).peer;
            arktsGlobal.compilerContext = arkts.Context.createFromStringWithHistory(source);
            plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program);
            arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer, this.skipDeclCheck);
            let ast = arkts.EtsScript.fromContext();
            plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
            plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.PARSED);
            arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer, this.skipDeclCheck);
            ast = arkts.EtsScript.fromContext();
            plugins_driver_1.PluginDriver.getInstance().getPluginContext().setArkTSAst(ast);
            plugins_driver_1.PluginDriver.getInstance().runPluginHook(plugins_driver_1.PluginHook.CHECKED);
            arkts.generateTsDeclarationsFromContext(declEtsOutputPath, etsOutputPath, false, false, staticRecordRelativePath, this.genDeclAnnotations); // Generate 1.0 declaration files & 1.0 glue code
            this.logger.printInfo('declaration files generated');
        }
        catch (error) {
            if (error instanceof Error) {
                throw new error_1.DriverError(logger_1.LogDataFactory.newInstance(error_1.ErrorCode.BUILDSYSTEM_DECLGEN_FAIL, 'Generate declaration files failed.', error.message, inputFilePath));
            }
        }
        finally {
            arktsGlobal.es2panda._DestroyContext(arktsGlobal.compilerContext.peer);
            arkts.destroyConfig(arktsGlobal.config);
        }
    }
}
exports.BaseMode = BaseMode;
